import argparse
import collections
import torch
import lib.metrics as module_metric
import model.dcrnn_model as module_arch
from parse_config import ConfigParser
from trainer.dcrnn_trainer import DCRNNTrainer
from lib import utils
import math
import os
import easydict


def train_main(config):
    logger = config.get_logger('train')
    print("[INFO] Loading {graph_pkl}".format(
        graph_pkl=config.graph_pkl_filename))
    _, _, adj_mat = utils.load_graph_data(config.graph_pkl_filename)
    data = utils.load_dataset(dataset_dir=config["dataloader"]["args"]["data_dir"],
                              batch_size=config["arch"]["args"]["batch_size"],
                              test_batch_size=config["arch"]["args"]["batch_size"])
    for k, v in data.items():
        if hasattr(v, 'shape'):
            print((k, v.shape))

    train_data_loader = data['train_loader']
    val_data_loader = data['val_loader']

    num_train_sample = data['x_train'].shape[0]
    num_val_sample = data['x_val'].shape[0]

    # get number of iterations per epoch for progress bar
    num_train_iteration_per_epoch = math.ceil(num_train_sample / config["arch"]["args"]["batch_size"])
    num_val_iteration_per_epoch = math.ceil(num_val_sample / config["arch"]["args"]["batch_size"])

    # setup data_loader instances
    # data_loader = config.initialize('data_loader', module_data)
    # valid_data_loader = data_loader.split_validation()

    # build model architecture, then print to console
    adj_arg = {"adj_mat": adj_mat}
    print("adj_arg=",adj_mat.shape)
    model = config.initialize('arch', module_arch, **adj_arg)
    # print("model=",model)
    # model = getattr(module_arch, config['arch']['type'])(config['arch']['args'], adj_arg)
    logger.info(model)
    print("num_nodes=",model.num_nodes)

    # get function handles of loss and metrics
    loss = config.initialize('loss', module_metric, **{"scaler": data['scaler']})
    # metrics = [getattr(module_metric, met) for met in config['metrics']]
    metrics = [getattr(module_metric, met) for met in config['metrics']['funs']]

    # build optimizer, learning rate scheduler. delete every lines containing lr_scheduler for disabling scheduler
    trainable_params = filter(lambda p: p.requires_grad, model.parameters())
    optimizer = config.initialize('optimizer', torch.optim, trainable_params)

    lr_scheduler = config.initialize('lr_scheduler', torch.optim.lr_scheduler, optimizer)

    trainer = DCRNNTrainer(model, loss, metrics, optimizer,
                           config=config,
                           data_loader=train_data_loader,
                           valid_data_loader=val_data_loader,
                           lr_scheduler=lr_scheduler,
                           len_epoch=num_train_iteration_per_epoch,
                           val_len_epoch=num_val_iteration_per_epoch)

    trainer.train()

def train_model(config,
                graph_pkl_filename=None,
                checkpoint_path=None,
                device=None,
                lr=None,
                batch_size=None
                ):
    # args = argparse.ArgumentParser(description='PyTorch DCRNN')
    # args.add_argument('-c', '--config', default=None, type=str,
    #                   help='config file path (default: None)')
    # args.add_argument('-g','--graph_pkl_filename',
    #                   default= None,
    #                   type=str,
    #                   help='graph pkl file path')
    #                   # 'data/sensor_graph/adj_mx_unix.pkl'
    # args.add_argument('-r', '--resume', default=None, type=str,
    #                   help='path to latest checkpoint (default: None)')
    # args.add_argument('-d', '--device', default=None, type=str,
    #                   help='indices of GPUs to enable (default: all)')
    args = easydict.EasyDict()
    args.graph_pkl_filename = None
    args.config = None
    args.resume = None
    args.device = None
    args.lr = None
    args.bs = None

    # arg_str = []
    if not os.path.exists(config):
        print("[ERROR] config file not exists")
    else:
        args.config = config
        # arg_str.append(" --config %s" % ( config ))

    if graph_pkl_filename is not None:
        if not os.path.exists(graph_pkl_filename):
            args.graph_pkl_filename = False
            print("[ERROR] graph pickle file not exists")
        else:
            args.graph_pkl_filename = graph_pkl_filename
            # arg_str.append(" -g %s" % ( graph_pkl_filename ))

    if checkpoint_path is not None:
        if not os.path.exists(checkpoint_path):
            args.resume = False
            print("[ERROR] checkpoint file not exists")
        else:
            args.resume = checkpoint_path
            # arg_str.append(" -r %s" % ( checkpoint_path ))

    if not isinstance(device,int) :
        if isinstance(device ,str):
            if not device.isdigit():
                print("[ERROR] device id is not integer")
            else:
                args.device =  device
        else:
            print("[ERROR] wrong type of device ID")

    else:
        args.device = str( device )
        # arg_str.append(" -d %s" % ( device ))
    if lr is not None:
        args.lr = lr

    if batch_size is not None:
        args.bs = batch_size


    CustomArgs = collections.namedtuple('CustomArgs', 'flags type target')
    options = [
        CustomArgs(['--lr', '--learning_rate'], type=float, target=('optimizer', 'args', 'lr')),
        CustomArgs(['--bs', '--batch_size'], type=int, target=('data_loader', 'args', 'batch_size'))
    ]
    # for opt in options:
    #     args.add_argument(*opt.flags, default=None, type=opt.type)
    # args_call = args.parse_args(args = arg_str)
    # args_call,unknown = args.parse_known_args(arg_str)
    config = ConfigParser(args, options)
    train_main(config)


if __name__ == '__main__':
    args = argparse.ArgumentParser(description='PyTorch DCRNN')
    args.add_argument('-c', '--config', default=None, type=str,
                      help='config file path (default: None)')
    args.add_argument('-g','--graph_pkl_filename',
                      default= None,
                      type=str,
                      help='graph pkl file path')
                      # 'data/sensor_graph/adj_mx_unix.pkl'
    args.add_argument('-r', '--resume', default=None, type=str,
                      help='path to latest checkpoint (default: None)')
    args.add_argument('-d', '--device', default=None, type=str,
                      help='indices of GPUs to enable (default: all)')

    # custom cli options to modify configuration from default values given in json file.
    CustomArgs = collections.namedtuple('CustomArgs', 'flags type target')
    options = [
        CustomArgs(['--lr', '--learning_rate'], type=float, target=('optimizer', 'args', 'lr')),
        CustomArgs(['--bs', '--batch_size'], type=int, target=('data_loader', 'args', 'batch_size'))
    ]
    for opt in options:
        args.add_argument(*opt.flags, default=None, type=opt.type)
    args_call = args.parse_args()
    config = ConfigParser(args_call, options)
    train_main(config)


